#pragma once
#include "lib/scutils.h"
#include "lib/conutils.h"

#define ACCOUNT_DATABASE "accounts.db"

enum LR_ERROR {
	ERONOE, // 没有错误
	EROULE, // 底层错误
	EROUNE, // 用户不存在
	ERORED, // 用户已经存在
	EROIVS, // 包含非法字符
	EROUVF // 身份验证失败
}; // 全局错误类


typedef struct {
	kvp* db; // 用户信息表
	string name; // 录入的用户名
	string pwd; // 录入的密码
	int ok; // 是否已登录
} LR_State; // 登录状态类

int Login_input(LR_State*); // 等待用户输入
int Login_verify(LR_State*); // 验证账号密码

void Login_regst(LR_State*); // 用户注册
void Login_login(LR_State*); // 用户登录

int login(); // 登录函数

// 初始化内存分配
#define Login_Linit(L) do { \
table __db = {}; (L).db = __db;\
(L).name = NULL;\
(L).pwd = NULL;\
(L).ok = 0;} while(0)

// 打印错误信息
#define Login_perror(E) do {switch(E){\
case EROULE:puts("[ERROR] 底层IO错误");break;\
case EROUNE:puts("[ERROR] 该用户不存在");break;\
case ERORED:puts("[ERROR] 该用户名已被占用");break;\
case EROIVS:puts("[ERROR] 不能包含空格");break;\
case EROUVF:puts("[ERROR] 账号或密码错误");break;\
} \
if (E) ifcontinue(); \
}while(0)



// ------- 具体实现 ------- //

int Login_input(LR_State* L) {
	string name = String.input("请输入用户名: ");

	if (*name->cstr == SC_SEP_CHAR || String.findchar(name, SC_SEP_CHAR))
		return EROIVS; // 不能包含空格

	string pwd = String.input("请输入密码: ");

	if (*pwd->cstr == SC_SEP_CHAR || String.findchar(pwd, SC_SEP_CHAR))
		return EROIVS; // 不能包含空格
	
	L->name = name;
	L->pwd = pwd;
	return ERONOE;
}


int Login_verify(LR_State* L) {
	char *true_pwd = tb_get(L->db, L->name->cstr);
	if (!true_pwd) 
		return EROUNE; // 用户不存在
	
	if (L->pwd != tostring(true_pwd)) {
		return EROUVF; // 账号或密码错误
	}
	
	L->ok = 1; // 登录成功
	return ERONOE;
}



// ------- 主要函数 ------- //

void Login_regst(LR_State* L) {
	int ECode = ERONOE;
	do {
		Login_perror(ECode);
		ECode = Login_input(L);
		if (ECode) continue;

		if (tb_get(L->db, L->name->cstr))
			ECode = ERORED; // 用户名已存在
		else {
			tb_set(L->db, L->name->cstr, L->pwd->cstr);
			puts("注册成功！");
			ECode = ERONOE;
		}
	} while (ECode);
}

void Login_login(LR_State* L) {
	int ECode = ERONOE;
	do {
		Login_perror(ECode);
		ECode = Login_input(L);
		if (ECode) continue;

		ECode = Login_verify(L);
		if (L->ok) {
			puts("登录成功！");
		}
	} while (ECode);
}

int login() {
	// 新建状态，检查数据库
	LR_State L;
	Login_Linit(L);
	touch(ACCOUNT_DATABASE);
	
	// 尝试打开数据库
	FILE* db_file = fopen(ACCOUNT_DATABASE, "r");
	if (!db_file) {
		puts("[ERROR] 打开账号数据库失败");
		return 0; // 以未登录状态退出
	}

	// 读取数据库内容
	string db_str = String.readfile(db_file);
	fclose(db_file);

	// 解析数据库内容
	if (sc_decode(db_str, L.db) < 0) {
		String.del(db_str);
		puts("[ERROR] 读取账号数据库失败");
		return 0; // 以未登录状态退出
	}
	String.del(db_str); // 删除临时字符串

	int account_num = tb_len(L.db); // 记录账号数量
	if (account_num == 0) {
		puts("[INFO] 账号数据库为空，请注册账号！");
	}
	
	// 引导用户执行登录或注册操作
	while (!L.ok) {
		puts("欢迎使用学生管理系统登录系统！");
		puts("1) 登录");
		puts("2) 注册");
		puts("3) 退出");
		printf("请选择操作：");
		int op;
		scanf("%d", &op);
		clearstdin();
		switch (op) {
		case 1:Login_login(&L); break;
		case 2:Login_regst(&L); break;
		case 3:goto save_and_clean;
		default:
			printf("选项 %d 不存在！\n", op);
			break;
		}
	}

	
	save_and_clean:

	// 数据库内容发生变化，需要保存
	if (tb_len(L.db) != account_num) {
		db_file = fopen(ACCOUNT_DATABASE, "w");
		if (!db_file) {
			puts("[ERROR] 保存账号数据库失败");
			return 0; // 以未登录状态退出
		}

		db_str = sc_encode(L.db);
		String.writefile(db_file, db_str);

		String.del(db_str);
		fclose(db_file);
	}

	String.gc();
	return L.ok;
}